#ifndef _META_TASK_H_
#define _META_TASK_H_

#include "Meta.Functor.h"
#include "Meta.Typelist.h"
#include "Meta.AbstractTask.h"

namespace Maha
{
      namespace Meta
      {

	template <class ResultType = void, class FunctorType = Functor<ResultType, NullType>, class TList = NullType>
	class Task;
	
	template <class ResultType, class FunctorType>
	class Task<ResultType, FunctorType, NullType> : public AbstractTask
	{
	public:
		Task(ResultType& result, FunctorType fctor)
			: result(result), fctor(fctor)
		{
		}
		
            virtual AbstractTask* Clone() const
            {
                  return new Task<ResultType, FunctorType, NullType>(*this);
            }
            
		void exec()
		{
			result = fctor();
		}
	private:
		FunctorType fctor;
		ResultType& result;
	};
	
	template <class ResultType, class FunctorType, class P1>
	class Task<ResultType, FunctorType, TYPELIST_1(P1)> : public AbstractTask
	{
	public:
	
		Task(ResultType& result, FunctorType fctor, P1 param1)
			: result(result), fctor(fctor), param1(param1) { }
		
            virtual AbstractTask* Clone() const
            {
                  return new Task<ResultType, FunctorType, TYPELIST_1(P1)>(*this);
            }
            
		void exec()
		{
			result = fctor(param1);
		}
	private:
		FunctorType fctor;
		ResultType& result;
		P1 param1;
	};

	template <class ResultType, class FunctorType, class P1, class P2>
	class Task<ResultType, FunctorType, TYPELIST_2(P1, P2)> : public AbstractTask
	{
	public:
	
		Task(ResultType& result, FunctorType fctor, P1 param1, P2 param2)
			: result(result), fctor(fctor), param1(param1), param2(param2) { }
		
            virtual AbstractTask* Clone() const
            {
                  return new Task<ResultType, FunctorType, TYPELIST_2(P1, P2)>(*this);
            }
            
		void exec()
		{
			result = fctor(param1, param2);
		}
            
	private:
		FunctorType fctor;
		ResultType& result;
		P1 param1;
		P2 param2;
	};
	
	
	template <class ResultType, class FunctorType, class P1, class P2, class P3>
	class Task<ResultType, FunctorType, TYPELIST_3(P1, P2, P3)> : public AbstractTask
	{
	public:
	
		Task(ResultType& result, FunctorType fctor, P1 param1)
			: result(result), fctor(fctor), param1(param1), param2(param2), param3(param3) { }
		
            virtual AbstractTask* Clone() const
            {
                  return new Task<ResultType, FunctorType, TYPELIST_3(P1, P2, P3)>(*this);
            }
                        
		void exec()
		{
			result = fctor(param1, param2, param3);
		}
            
	private:
		FunctorType fctor;
		ResultType& result;
		P1 param1;
		P2 param2;
		P3 param3;
	};
	
	template <class ResultType, class FunctorType, class P1, class P2, class P3, class P4>
	class Task<ResultType, FunctorType, TYPELIST_4(P1, P2, P3, P4)> : public AbstractTask
	{
	public:
	
		Task(ResultType& result, FunctorType fctor, P1 param1)
			: result(result), fctor(fctor), param1(param1), param2(param2), param3(param3), param4(param4) { }
		
            virtual AbstractTask* Clone() const
            {
                  return new Task<ResultType, FunctorType, TYPELIST_4(P1, P2, P3, P4)>(*this);
            }
            
		void exec()
		{
			result = fctor(param1, param2, param3, param4);
		}
	private:
		FunctorType fctor;
		ResultType& result;
		P1 param1;
		P2 param2;
		P3 param3;
		P4 param4;
	};
	
	template <class FunctorType>
	class Task<void, FunctorType, NullType> : public AbstractTask
	{
	public:
	
		Task(FunctorType fctor) : fctor(fctor) { }
		
            virtual AbstractTask* Clone() const
            {
                  return new Task<void, FunctorType, NullType>(*this);
            }
                        
		void exec()
		{
			fctor();
		}
		
	private:
		FunctorType fctor;
	};
	
	template <class FunctorType, class P1>
	class Task<void, FunctorType, TYPELIST_1(P1)> : public AbstractTask
	{
	public:

	
		Task(FunctorType fctor, P1 param1)
			: fctor(fctor), param1(param1) { }
		
            virtual AbstractTask* Clone() const
            {
                  return new Task<void, FunctorType, TYPELIST_1(P1)>(*this);
            }
            
		void exec()
		{
			fctor(param1);
		}
		
	private:
		FunctorType fctor;
		P1 param1;
	};

	template <class FunctorType, class P1, class P2>
	class Task<void, FunctorType, TYPELIST_2(P1, P2)> : public AbstractTask
	{
	public:
	
		Task(FunctorType fctor, P1 param1, P2 param2)
			: fctor(fctor), param1(param1), param2(param2) { }
		
            virtual AbstractTask* Clone() const
            {
                  return new Task<void, FunctorType, TYPELIST_2(P1, P2)>(*this);
            }
                        
		void exec()
		{
			fctor(param1, param2);
		}
	private:
		FunctorType fctor;
		P1 param1;
		P2 param2;
	};
	
	
	template <class FunctorType, class P1, class P2, class P3>
	class Task<void, FunctorType, TYPELIST_3(P1, P2, P3)> : public AbstractTask
	{
	public:
	
		Task(FunctorType fctor, P1 param1)
			: fctor(fctor), param1(param1), param2(param2), param3(param3) { }
		
            virtual AbstractTask* Clone() const
            {
                  return new Task<void, FunctorType, TYPELIST_3(P1, P2, P3)>(*this);
            }
            
		void exec()
		{
			fctor(param1, param2, param3);
		}
	private:
		FunctorType fctor;
		P1 param1;
		P2 param2;
		P3 param3;
	};
	
	template <class FunctorType, class P1, class P2, class P3, class P4>
	class Task<void, FunctorType, TYPELIST_4(P1, P2, P3, P4)> : public AbstractTask
	{
	public:
	
		Task(FunctorType fctor, P1 param1)
			: fctor(fctor), param1(param1), param2(param2), param3(param3), param4(param4) { }
		
            virtual AbstractTask* Clone() const
            {
                  return new Task<void, FunctorType, TYPELIST_4(P1, P2, P3, P4)>(*this);
            }
            
		void exec()
		{
			fctor(param1, param2, param3, param4);
		}
	private:
		FunctorType fctor;
		P1 param1;
		P2 param2;
		P3 param3;
		P4 param4;
	};
	
      } // Maha
} // Meta

#endif //_META_TASK_H_
